package com.funcourse.demo.api.restful;

import com.funcourse.demo.api.restful.io.response.ApiResponse;
import com.funcourse.demo.api.restful.io.response.error.CommonUsedError;
import com.funcourse.demo.api.restful.io.response.error.CustomizedError;
import com.funcourse.demo.api.restful.io.response.busi.MeVO;
import com.funcourse.demo.business.RedisResourceLock;
import com.funcourse.demo.business.UserService;
import com.funcourse.demo.sms.vcode.VerifyCodeFunction;
import com.funcourse.demo.sms.vcode.VerifyCodeManager;
import com.funcourse.demo.sms.vcode.VerifyCodeMongoBean;
import com.funcourse.demo.model.User;
import com.funcourse.demo.model.repo.UserRepo;
import com.funcourse.demo.wechat.client.web.UserInfoGetter;
import com.funcourse.demo.wechat.client.web.io.resp.JsCode2TokenResponse;
import com.funcourse.demo.wechat.client.web.JsCode2TokenGetter;
import com.funcourse.demo.wechat.client.web.io.resp.QueryUserInfoResponse;
import com.funcourse.demo.wechat.client.welet.CodeToSessionCaller;
import com.funcourse.demo.wechat.client.welet.Js2SessionResponse;
import io.swagger.annotations.ApiOperation;
import java.util.List;
import java.util.Set;
import javax.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@CrossOrigin(origins = "*")
@RestController
@RequestMapping(path = "#{'/api/'+'${service.version}'+'/login'}")
public class LoginController {

  private static final String LOGIN_LOCK_KEY_PREFIX = "funcourse.login.lock.";

  RedisResourceLock redisResourceLock;

  @Autowired
  RedisTemplate redisTemplate;

  @Autowired
  JsCode2TokenGetter jsCode2TokenGetter;

  @Autowired
  CodeToSessionCaller codeToSessionCaller;

  @Autowired
  UserInfoGetter userInfoGetter;

  @Autowired
  UserRepo userRepo;

  @Autowired
  VerifyCodeManager verifyCodeManager;

  @Autowired
  UserService userService;

  @PostMapping("/smsLogin")
  @ApiOperation(value = "短信登录")
  public ApiResponse<MeVO> smsLogin(String mobile,String code){
    VerifyCodeMongoBean verifyCodeMongoBean = verifyCodeManager
        .doVerifyCode(mobile, VerifyCodeFunction.LOGIN, code);
    if(null == verifyCodeMongoBean){
      return new ApiResponse<MeVO>(CommonUsedError.BAD_REQUEST,null);
    }
    redisResourceLock.tryToGetResourceLockBlocking(mobile);
    User byMobile = userRepo.findByMobile(mobile);
    if(null == byMobile){
      byMobile = userService.registerNewUser();
      byMobile.setNickName(mobile.substring(7));
      byMobile.setMobile(mobile);
      userRepo.save(byMobile);
    }
    redisResourceLock.releaseResourceLock(mobile);
    return new ApiResponse<>(CommonUsedError.SUCCESS,new MeVO(byMobile));
  }

  @PostMapping("/wechatLogin")
  @ApiOperation(value = "微信网站登录")
  public ApiResponse<MeVO> wechatLogin(String jsCode,String state) {
    JsCode2TokenResponse js2SessionResponse = jsCode2TokenGetter.code2Session(jsCode);
    if (js2SessionResponse.success()) {
      //每次登录都更新用户昵称和头像
      QueryUserInfoResponse userInfo = userInfoGetter
          .getUserInfo(js2SessionResponse.getAccessToken(), js2SessionResponse.getOpenId());
      if (userInfo.success()) {
        String unionId = userInfo.getUnionId();
        String openId = js2SessionResponse.getOpenId();
        final String lockResourceId = unionId + openId;
        redisResourceLock.tryToGetResourceLockBlocking(lockResourceId);
        User user;
        User wechatUser;
        if (null != unionId) {
          wechatUser = userRepo.findByUnionId(unionId);
        } else {
          wechatUser = userRepo.findByWebOpenId(openId);
        }
        //手机号用户增加微信信息
        if(!StringUtils.isEmpty(state)){
          user = userRepo.findByMobile(state);
          if(null == user){
            redisResourceLock.releaseResourceLock(lockResourceId);
            return new ApiResponse<>(CommonUsedError.UNAUTHORIZED.customizedError("非法的手机号"), null);
          }
          if(null != wechatUser){
            redisResourceLock.releaseResourceLock(lockResourceId);
            return new ApiResponse<>(CommonUsedError.UNAUTHORIZED.customizedError("无法绑定到已存在的微信用户"), null);
          }

        }else{
          //单纯的微信登录
          user = wechatUser;
        }

        if (null == user) {
          user = userService.registerNewUser();
        }
        user.setWebOpenId(openId);
        if(userService.avatarUrlCouldCover(user.getAvatarUrl())){
          user.setAvatarUrl(userInfo.getHeadImgUrl());
        }
        if(null == user.getNickName()){
          user.setNickName(userInfo.getNickName());
        }
        user.setUnionId(unionId);
        userRepo.save(user);
        redisResourceLock.releaseResourceLock(lockResourceId);
        return new ApiResponse<>(CommonUsedError.SUCCESS, new MeVO(user));
      } else {
        log.error("get wechat user info failed with code {} and msg {}", userInfo.getErrCode(),
            userInfo.getErrMsg());
        return new ApiResponse<>(new CustomizedError(userInfo.getErrCode(), userInfo.getErrMsg()), null);
      }

    } else {
      log.error("wechat user jsCode2token failed with code {} and msg {}",
          js2SessionResponse.getErrCode(), js2SessionResponse.getErrMsg());
      return new ApiResponse<>(
          new CustomizedError(js2SessionResponse.getErrCode(), js2SessionResponse.getErrMsg()),
          null);
    }
  }

  @PostMapping("/weletLogin")
  @ApiOperation(value = "微信小程序登录")
  public ApiResponse<MeVO> weletLogin(String jsCode) {
    Js2SessionResponse js2SessionResponse = codeToSessionCaller.code2Session(jsCode);
    if (js2SessionResponse.success()) {
      String unionid = js2SessionResponse.getUnionid();
      String openId = js2SessionResponse.getOpenId();
      final String lockResourceId = unionid + openId;
      redisResourceLock.tryToGetResourceLockBlocking(lockResourceId);
      log.debug("welet user login with openId  {} and unionId",openId,unionid);
      User user;
      if(null != unionid){
        user = userRepo.findByUnionId(unionid);
      }else{
        user = userRepo.findByWeletOpenIds(openId);
        if(null == user){
          user = userRepo.findByWeletOpenId(openId);
        }
      }
      log.debug("user found {}",user == null ? null : user.getId());
      if (null == user) {
        user = userService.registerNewUser();
      }
      final Set<String> weletOpenIds = user.getWeletOpenIds();
      weletOpenIds.add(openId);
      user.setSessionKey(js2SessionResponse.getSessionKey());
      if(null != unionid){
        user.setUnionId(unionid);
      }
      userRepo.save(user);
      redisResourceLock.releaseResourceLock(lockResourceId);
      return new ApiResponse<>(CommonUsedError.SUCCESS, new MeVO(user));
    } else {
      log.error("welet user {} jsCode2token failed with code {} and msg {}", jsCode,
          js2SessionResponse.getErrCode(), js2SessionResponse.getErrMsg());
      return new ApiResponse<>(
          new CustomizedError(js2SessionResponse.getErrCode(), js2SessionResponse.getErrMsg()),
          null);
    }

  }


  @PostMapping("/cardNumberLogin")
  @ApiOperation(value = "卡号登录")
  public ApiResponse<MeVO> cardNumberLogin(String cardNumber,String organizationId,String password) {
    if(!"12er45(I)O".equals(password)){
      return new ApiResponse<>(
          CommonUsedError.UNAUTHORIZED,
          null);
    }
    String lockResourceId = organizationId + cardNumber;
    redisResourceLock.tryToGetResourceLockBlocking(lockResourceId);
    final User user = userRepo
        .findByCardNumberAndOrganizationId(cardNumber, organizationId);
    if(null == user){
      redisResourceLock.releaseResourceLock(lockResourceId);
      return new ApiResponse<>(
          CommonUsedError.UNAUTHORIZED,
          null);
    }
    redisResourceLock.releaseResourceLock(lockResourceId);
    return new ApiResponse<>(CommonUsedError.SUCCESS, new MeVO(user));
  }

  @PostConstruct
  public void initRedisResourceLock(){
    redisResourceLock = new RedisResourceLock(LOGIN_LOCK_KEY_PREFIX,redisTemplate);
  }

}
